import aima.core.agent.Action;
import aima.core.agent.impl.DynamicAction;

/** 
 * Estado del Problema, define los elementos que participan:
 */
public class EstadoMisioneros {
	private int numMisioneros; // Misioneros en orilla izquierda
	private int numCanibales; // Canibales en orilla izquierda
	private boolean barcaIzq; // true si barca en orilla izquierda


	// Acciones posibles (actions) como objetos DynamicAction

	public static Action M = new DynamicAction("M");
	public static Action MM = new DynamicAction("MM");
	public static Action C = new DynamicAction("C");
	public static Action CC = new DynamicAction("CC");
	public static Action MC = new DynamicAction("MC");

	public EstadoMisioneros() {
		this(3, 3, true);
	}
	
	public EstadoMisioneros(EstadoMisioneros mc) {
		this(mc.getnMisioneros(), mc.getnCanibales(), mc.isBarcaIzq());
	}
	
	public EstadoMisioneros(int nMisioneros, int nCanibales, boolean barcaIzq) {
		this.numMisioneros = nMisioneros;
		this.numCanibales = nCanibales;
		this.barcaIzq = barcaIzq;
	}
	
	public int getnMisioneros() {
		return numMisioneros;
	}
	
	public int getnCanibales() {
		return numCanibales;
	}
	
	public boolean isBarcaIzq() {
		return barcaIzq;
	}
	
	public void moveM() {
		if (barcaIzq)
			numMisioneros--;	
		else
			numMisioneros++;
		cambiarDeOrilla();
	}
	
	public void moveMM() {
		if (barcaIzq)
			numMisioneros-=2;	
		else
			numMisioneros+=2;
		cambiarDeOrilla();
	}
	
	public void moveC() {
		if (barcaIzq)
			numCanibales--;	
		else
			numCanibales++;
		cambiarDeOrilla();
	}
	
	public void moveCC() {
		if (barcaIzq)
			numCanibales-=2;	
		else
			numCanibales+=2;
		cambiarDeOrilla();
	}
	
	public void moveMC() {
		if (barcaIzq) {
			numMisioneros--;
			numCanibales--;
		}
		else {
			numMisioneros++;
			numCanibales++;
		}
		cambiarDeOrilla();
	}
	
	/**
	 * Movimiento de la barca
	 */
	public void cambiarDeOrilla() {
		if (barcaIzq)
			barcaIzq = false;
		else
			barcaIzq = true;
	}
	
	/**
	 * Movimietos permitidos (operadores validos)
	 * @param where
	 * @return
	 */
	public boolean movimientoValido(Action where) {
		boolean valido = false;
		if (where.equals(M)) {
			if ((barcaIzq && numMisioneros > 0) || (!barcaIzq && numMisioneros < 3)) {
					EstadoMisioneros estadoSiguiente = new EstadoMisioneros(this);
					estadoSiguiente.moveM();
					valido = !estadoSiguiente.peligro();
			}
			else valido = false;
		}
		else if (where.equals(MM)) {
			if ((barcaIzq && numMisioneros > 1) || (!barcaIzq && numMisioneros < 2)) {
				EstadoMisioneros estadoSiguiente = new EstadoMisioneros(this);
				estadoSiguiente.moveMM();
				valido = !estadoSiguiente.peligro();
			}
			else valido = false;
		}
		else if (where.equals(C)) {
			if ((barcaIzq && numCanibales > 0) || (!barcaIzq && numCanibales < 3)) {
				EstadoMisioneros estadoSiguiente = new EstadoMisioneros(this);
				estadoSiguiente.moveC();
				valido = !estadoSiguiente.peligro();
			}
			else valido = false;
		}
		else if (where.equals(CC)) {
			if ((barcaIzq && numCanibales > 1) || (!barcaIzq && numCanibales < 2)) {
				EstadoMisioneros estadoSiguiente = new EstadoMisioneros(this);
				estadoSiguiente.moveCC();
				valido = !estadoSiguiente.peligro();
			}
			else valido = false;
		}
		else {
			if ((barcaIzq && numCanibales > 0 && numMisioneros > 0) || (!barcaIzq && numCanibales < 3 && numMisioneros < 3)) {
				EstadoMisioneros estadoSiguiente = new EstadoMisioneros(this);
				estadoSiguiente.moveMC();
				valido = !estadoSiguiente.peligro();
			}
			else valido = false;
		}
		return valido;
	}
	
	/** 
	 * Estados de peligro
	 * @return true = peligro, false = no peligro
	 */
	private boolean peligro() {
		if (((numMisioneros < numCanibales) && (numMisioneros != 0))
				|| ((numMisioneros > numCanibales) && (numMisioneros != 3)))
				return true;
		else
				return false;
	}
	
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if ((o == null) || (this.getClass() != o.getClass())) {
			return false;
		}
		EstadoMisioneros otroEstado = (EstadoMisioneros) o;
		if ((this.numMisioneros == otroEstado.getnMisioneros()) &&
		(this.numCanibales == otroEstado.getnCanibales()) &&
		(this.barcaIzq == otroEstado.isBarcaIzq()))
			return true;
		else
			return false;
	}
	
	public int hashCode() {
		return (100 * numMisioneros) + (10 * numCanibales) + (barcaIzq ? 1 : 0);
	}	
}
